/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.loaders; import java.awt.event.KeyEvent; import java.io.IOException; import java.lang.ref.*; import java.util.StringTokenizer; import java.beans.*; import java.util.Enumeration; import java.text.MessageFormat; import javax.swing.tree.TreeSelectionModel; import javax.swing.tree.TreePath; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; import javax.swing.ListSelectionModel; import javax.swing.JButton; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import org.openide.TopManager; import org.openide.DialogDescriptor; import org.openide.WizardDescriptor; import org.openide.loaders.*; import org.openide.nodes.*; import org.openide.explorer.view.*; import org.openide.explorer.ExplorerManager; import org.openide.filesystems.*; import org.openide.util.UserCancelException; import org.openide.util.HelpCtx; import org.openide.util.Mutex; import org.openide.util.Utilities; import org.openide.util.enum.*; /** Dialog that can be used in create from template. * * @author Jaroslav Tulach * @version */ final class TemplateWizard2 extends javax.swing.JPanel implements DocumentListener, WizardDescriptor.FinishPanel, DataFilter, PropertyChangeListener, VetoableChangeListener { /** format to for default package */ private static MessageFormat defaultPackageName; /** listener to changes in the wizard */ private ChangeListener listener; /** system reference (FileSystem) */ private Reference system = new WeakReference (null); /** root node */ private Node rootNode; /** Creates new form NewFromTemplatePanel */ public TemplateWizard2 () { initComponents (); setName (org.openide.util.NbBundle.getBundle(TemplateWizard2.class).getString("LAB_TargetLocationPanelName")); setBorder (new javax.swing.border.EmptyBorder(new java.awt.Insets(8, 8, 8, 8))); packagesPanel.setBorder (new javax.swing.border.CompoundBorder( new javax.swing.border.TitledBorder(org.openide.util.NbBundle.getBundle(TemplateWizard2.class).getString("LAB_SelectPackageBorder")), new javax.swing.border.EmptyBorder(new java.awt.Insets(8, 8, 8, 8))) ); rootNode = createPackagesNode (); packagesPanel.getExplorerManager ().setRootContext (rootNode); packagesPanel.getExplorerManager ().addPropertyChangeListener (this); packagesPanel.getExplorerManager ().addVetoableChangeListener (this); // registers itself to listen to changes in the content of document packageName.getDocument().addDocumentListener(this); packageName.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)); // registers itself to listen to changes in the content of document className.getDocument().addDocumentListener(this); className.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)); descriptionLabel.setText (org.openide.util.NbBundle.getBundle(TemplateWizard2.class).getString("LAB_TargetLocationDescription")); } /** Preffered size */ public java.awt.Dimension getPreferredSize() { return TemplateWizard.PREF_DIM; } /** Request focus. */ public void requestFocus () { className.requestFocus(); className.selectAll (); } /** Creates node that displays all packages. */ private Node createPackagesNode () { return TopManager.getDefault().getPlaces().nodes ().repository( this ); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the FormEditor. */ private void initComponents () {//GEN-BEGIN:initComponents namePanel = new javax.swing.JPanel (); jLabel1 = new javax.swing.JLabel (); className = new javax.swing.JTextField (); packagesPanel = new org.openide.explorer.ExplorerPanel (); packageNamePanel = new javax.swing.JPanel (); jLabel2 = new javax.swing.JLabel (); packageName = new javax.swing.JTextField (); beanTreeView = new org.openide.explorer.view.BeanTreeView (); descriptionLabel = new javax.swing.JLabel (); setLayout (new java.awt.BorderLayout (0, 8)); namePanel.setLayout (new java.awt.GridBagLayout ()); java.awt.GridBagConstraints gridBagConstraints1; jLabel1.setText (java.util.ResourceBundle.getBundle("org/openide/loaders/Bundle").getString("CTL_TemplateClassName")); gridBagConstraints1 = new java.awt.GridBagConstraints (); gridBagConstraints1.insets = new java.awt.Insets (0, 0, 0, 8); gridBagConstraints1.anchor = java.awt.GridBagConstraints.WEST; namePanel.add (jLabel1, gridBagConstraints1); className.addFocusListener (new java.awt.event.FocusAdapter () { public void focusGained (java.awt.event.FocusEvent evt) { classNameFocusGained (evt); } } ); gridBagConstraints1 = new java.awt.GridBagConstraints (); gridBagConstraints1.gridwidth = 0; gridBagConstraints1.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints1.weightx = 1.0; namePanel.add (className, gridBagConstraints1); add (namePanel, java.awt.BorderLayout.NORTH); packagesPanel.setLayout (new java.awt.BorderLayout (0, 8)); packageNamePanel.setLayout (new java.awt.GridBagLayout ()); java.awt.GridBagConstraints gridBagConstraints2; jLabel2.setText (java.util.ResourceBundle.getBundle("org/openide/loaders/Bundle").getString("CTL_TemplatePackageName")); gridBagConstraints2 = new java.awt.GridBagConstraints (); packageNamePanel.add (jLabel2, gridBagConstraints2); packageName.addFocusListener (new java.awt.event.FocusAdapter () { public void focusGained (java.awt.event.FocusEvent evt) { packageNameFocusGained (evt); } } ); gridBagConstraints2 = new java.awt.GridBagConstraints (); gridBagConstraints2.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints2.insets = new java.awt.Insets (0, 8, 0, 0); gridBagConstraints2.weightx = 1.0; packageNamePanel.add (packageName, gridBagConstraints2); packagesPanel.add (packageNamePanel, java.awt.BorderLayout.SOUTH); beanTreeView.setPopupAllowed (false); packagesPanel.add (beanTreeView, java.awt.BorderLayout.CENTER); packagesPanel.add (descriptionLabel, java.awt.BorderLayout.NORTH); add (packagesPanel, java.awt.BorderLayout.CENTER); }//GEN-END:initComponents private void packageNameFocusGained (java.awt.event.FocusEvent evt) {//GEN-FIRST:event_packageNameFocusGained packageName.selectAll (); }//GEN-LAST:event_packageNameFocusGained private void packageModelChanged (javax.swing.event.ListDataEvent evt) {//GEN-FIRST:event_packageModelChanged }//GEN-LAST:event_packageModelChanged private void classNameFocusGained (java.awt.event.FocusEvent evt) {//GEN-FIRST:event_classNameFocusGained if ( Utilities.getOperatingSystem() == Utilities.OS_SOLARIS || Utilities.getOperatingSystem() == Utilities.OS_SUNOS ) { // does not work on CDE window manager, so better do nothing return; } className.selectAll (); }//GEN-LAST:event_classNameFocusGained private void templatesTreeValueChanged (javax.swing.event.TreeSelectionEvent evt) {//GEN-FIRST:event_templatesTreeValueChanged }//GEN-LAST:event_templatesTreeValueChanged // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel namePanel; private javax.swing.JLabel jLabel1; private javax.swing.JTextField className; private org.openide.explorer.ExplorerPanel packagesPanel; private javax.swing.JPanel packageNamePanel; private javax.swing.JLabel jLabel2; private javax.swing.JTextField packageName; private org.openide.explorer.view.BeanTreeView beanTreeView; private javax.swing.JLabel descriptionLabel; // End of variables declaration//GEN-END:variables // // Filter to accept only folders // /** Should the data object be displayed or not? * @param obj the data object * @return <CODE>true</CODE> if the object should be displayed, * <CODE>false</CODE> otherwise */ public boolean acceptDataObject(DataObject obj) { return obj instanceof DataFolder; } /** Allow only simple selection. */ public void vetoableChange(PropertyChangeEvent ev) throws PropertyVetoException { if (ExplorerManager.PROP_SELECTED_NODES.equals (ev.getPropertyName ())) { Node[] arr = packagesPanel.getExplorerManager ().getSelectedNodes (); if (arr.length > 1) { throw new PropertyVetoException ("Only single selection allowed", ev); // NOI18N } } } /** Changes in selected node in packages. */ public void propertyChange (PropertyChangeEvent ev) { if (ExplorerManager.PROP_SELECTED_NODES.equals (ev.getPropertyName ())) { Node[] arr = packagesPanel.getExplorerManager ().getSelectedNodes (); if (arr.length == 1) { DataFolder df = (DataFolder)arr[0].getCookie (DataFolder.class); if (df != null) { setTargetFolder (df); return; } } setTargetFolder ((DataFolder)null); } } /** Fires info to listener. */ private void fireStateChanged () { if (listener != null) { listener.stateChanged (new ChangeEvent (this)); } } // // Modification of package name or class name // public void changedUpdate(final javax.swing.event.DocumentEvent p1) { if (p1.getDocument () == packageName.getDocument ()) { SwingUtilities.invokeLater (new Runnable () { public void run () { String text = packageName.getText (); if (text != null) { setTargetFolder (text); } } }); return; } if (p1.getDocument () == className.getDocument ()) { fireStateChanged (); } } public void removeUpdate(final javax.swing.event.DocumentEvent p1) { // when deleted => do no looking for folder // changedUpdate (p1); if (p1.getDocument () == packageName.getDocument ()) { SwingUtilities.invokeLater(new Runnable () { public void run () { if (packageName.getText ().length () == 0) { FileSystem fs = (FileSystem)system.get (); if (fs != null) { DataFolder df = DataFolder.findFolder (fs.getRoot ()); setTargetFolder (df); packageName.selectAll (); } } } }); } // so just check the name if (p1.getDocument () == className.getDocument ()) { fireStateChanged (); } } public void insertUpdate(final javax.swing.event.DocumentEvent p1) { changedUpdate (p1); } // // Wizard // /** Get the component displayed in this panel. * @return the component */ public java.awt.Component getComponent () { return this; } /** Help for this panel. * @return the help or <code>null</code> if no help is supplied */ public org.openide.util.HelpCtx getHelp () { return new HelpCtx (TemplateWizard2.class); } /** Provides the wizard panel with the current data--either * the default data or already-modified settings, if the user used the previous and/or next buttons. * This method can be called multiple times on one instance of <code>WizardDescriptor.Panel</code>. * @param settings the object representing wizard panel state, as originally supplied to {@link WizardDescriptor#WizardDescriptor(WizardDescriptor.Iterator,Object)} */ public void readSettings (Object settings) { TemplateWizard wizard = (TemplateWizard)settings; className.setText (wizard.getClassName ()); String name = wizard.getPackageName (); FileSystem fs = wizard.getSystem (); system = new WeakReference (fs); requestFocus (); // set the current target folder. if (fs != null && name != null) { FileObject fo = fs.find (name, null, null); if (fo != null) { // try it by folder setTargetFolder (DataFolder.findFolder (fo)); return; } } // or if the folder has not been found by name setTargetFolder (name); } /** Provides the wizard panel with the opportunity to update the * settings with its current customized state. * Rather than updating its settings with every change in the GUI, it should collect them, * and then only save them when requested to by this method. * Also, the original settings passed to {@link #readSettings} should not be modified (mutated); * rather, the (copy) passed in here should be mutated according to the collected changes. * This method can be called multiple times on one instance of <code>WizardDescriptor.Panel</code>. * @param settings the object representing a settings of the wizard */ public void storeSettings (Object settings) { TemplateWizard wizard = (TemplateWizard)settings; FileSystem fs = (FileSystem)system.get (); String name = packageName.getText (); if (name.equals (defaultPackageName (fs))) { name = ""; } wizard.setNameSystem (name, fs); wizard.setTargetName (className.getText ()); } /** Test whether the panel is finished and it is safe to proceed to the next one. * If the panel is valid, the "Next" (or "Finish") button will be enabled. * @return <code>true</code> if the user has entered satisfactory information */ public boolean isValid () { if (!Utilities.isJavaIdentifier(className.getText ())) return false; String text = packageName.getText (); if (text.length () == 0) { Node[] arr = packagesPanel.getExplorerManager ().getSelectedNodes (); if (arr.length == 1 && arr[0] == rootNode) { return false; } } if (text.equals (defaultPackageName ((FileSystem)system.get ()))) { return true; } StringTokenizer tok = new StringTokenizer (text, "."); // NOI18N while (tok.hasMoreElements ()) { String pkg = tok.nextToken (); if (!Utilities.isJavaIdentifier (pkg)) { return false; } } return true; } /** Add a listener to changes of the panel's validity. * @param l the listener to add * @see #isValid */ public void addChangeListener (ChangeListener l) { if (listener != null) throw new IllegalStateException (); listener = l; } /** Remove a listener to changes of the panel's validity. * @param l the listener to remove */ public void removeChangeListener (ChangeListener l) { listener = null; } /** Computes a suggestion for a given prefix and * a list of file objects. * * @param node the node to start with * @param pref prefix * @param first [0] is the first node that satisfies the suggestion * @return the longest continuation string for all folders that * starts with prefix */ private static String computeSuggestion ( Node node, String pref, Node[] first ) { Node[] arr = node.getChildren ().getNodes (); String match = null; for (int i = 0; i < arr.length; i++) { String name = arr[i].getName (); if (name.startsWith (pref)) { // ok, has the right prefix if (match == null) { // first match match = name; first[0] = arr[i]; } else { // find common part of the names int indx = pref.length (); int end = Math.min (name.length (), match.length ()); while (indx < end && match.charAt (indx) == name.charAt (indx)) { indx++; } match = match.substring (0, indx); } } } if (match == null || match.length () == pref.length ()) { return null; } else { return match.substring (pref.length ()); } } /** Presets a target folder. * @param f the folder * @return true if succeeded */ private boolean setTargetFolder (final DataFolder f) { boolean exact; Node n; String name; if (f != null) { FileObject fo = f.getPrimaryFile (); name = fo.getPackageName('.'); StringTokenizer st = new StringTokenizer (name, "."); // NOI18N try { FileSystem fs = fo.getFileSystem (); if (fo.isRoot ()) { name = defaultPackageName (fs); } system = new WeakReference (fs); Enumeration en = new SequenceEnumeration ( new SingletonEnumeration (fs.getSystemName()), st ); n = NodeOp.findPath (rootNode, en); exact = true; } catch (FileStateInvalidException ex) { // invalid state of file system => back to root n = rootNode; name = ""; // NOI18N exact = false; } catch (NodeNotFoundException ex) { n = ex.getClosestNode(); DataFolder df = (DataFolder)n.getCookie (DataFolder.class); if (df != null) { name = df.getPrimaryFile ().getPackageName ('.'); } else { name = ""; } exact = false; } } else { // null folder => use root n = rootNode; name = null; exact = true; } // remove listener + do change + add listener ExplorerManager em = packagesPanel.getExplorerManager (); em.removePropertyChangeListener (this); packageName.getDocument ().removeDocumentListener (this); try { em.setSelectedNodes (new Node[] { n }); } catch (PropertyVetoException ex) { throw new InternalError (); } packageName.setText (name); packageName.getDocument ().addDocumentListener (this); em.addPropertyChangeListener (this); fireStateChanged (); return exact; } /** Presets a target folder. * @param f the name of target folder * @return true if succeeded */ private boolean setTargetFolder (final String f) { Node n = null; NodeNotFoundException closest = null; Node[] arr = rootNode.getChildren ().getNodes (); for (int i = 0; i < arr.length; i++) { Node root = arr[i]; StringTokenizer st = new StringTokenizer (f, "."); // NOI18N try { n = NodeOp.findPath (root, st); break; } catch (NodeNotFoundException ex) { if (closest == null && !st.hasMoreElements ()) { // a test for !hasMoreElements is here to be sure that // all tokens has been read, so only the last item // has not been found closest = ex; } } } if (n != null) { // closest node not used closest = null; } else { if (closest == null) { // the node has not been even found return false; } // we will select the closest node found n = closest.getClosestNode (); } // remove listener + do change + add listener ExplorerManager em = packagesPanel.getExplorerManager (); em.removePropertyChangeListener (this); // change the text if we want to add suggestion if (closest != null) { Node[] first = new Node[1]; final String sugg = computeSuggestion ( closest.getClosestNode (), closest.getMissingChildName(), first ); if (sugg != null) { packageName.getDocument ().removeDocumentListener ( TemplateWizard2.this ); packageName.setText (f + sugg); javax.swing.text.Caret c = packageName.getCaret (); c.setDot (f.length () + sugg.length ()); c.moveDot (f.length ()); packageName.getDocument ().addDocumentListener ( TemplateWizard2.this ); } if (first[0] != null) { // show the first node that fits n = first[0]; } } // change the node try { em.setSelectedNodes (new Node[] { n }); } catch (PropertyVetoException ex) { throw new InternalError (); } // change the selected filesystem DataFolder df = (DataFolder)n.getCookie (DataFolder.class); if (df != null) { try { FileSystem fs = df.getPrimaryFile ().getFileSystem (); system = new WeakReference (fs); } catch (FileStateInvalidException ex) { } } em.addPropertyChangeListener (this); fireStateChanged (); return closest == null; } /** Creates default package name for given file system. * @param fs the file system * @return localized name of default package */ private static String defaultPackageName (FileSystem fs) { if (defaultPackageName == null) { defaultPackageName = new MessageFormat (DataObject.getString ( "FMT_TemplateDefaultPackageName" )); } String n = fs == null ? "" : fs.getDisplayName (); return defaultPackageName.format (new Object[] { n }); } }